#ifndef SANITIZER_INTERCEPTORS_H
#define SANITIZER_INTERCEPTORS_H

#include "asan_internal_defs.h"
#include "string.h"


# define PTR_TO_REAL(x) real_##x
# define REAL(x) __sanitizer::PTR_TO_REAL(x)
# define FUNC_TYPE(x) x##_type

#define DEFINE_REAL(ret_type, func, ...)            \
  typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
  namespace __sanitizer {                        \
  FUNC_TYPE(func) PTR_TO_REAL(func);                \
}

# define DECLARE_WRAPPER(ret_type, func, ...) \
    extern "C" ret_type func(__VA_ARGS__) \
    __attribute__((weak, alias("__interceptor_" #func), visibility("default")));

# define DECLARE_CXX_WRAPPER(ret_type, func, ...) \
    ret_type func(__VA_ARGS__) \
    __attribute__((weak, alias("__interceptor_" #func), visibility("default")));

# define WRAP(x) __interceptor_ ## x
# define WRAPPER_NAME(x) "__interceptor_" #x
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))

#define INTERCEPTOR(ret_type, func, ...) \
  DEFINE_REAL(ret_type, func, __VA_ARGS__) \
  DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
  extern "C" \
  INTERCEPTOR_ATTRIBUTE \
  ret_type WRAP(func)(__VA_ARGS__)

#define CXX_INTERCEPTOR(ret_type, func, ...) \
  DEFINE_REAL(ret_type, func, __VA_ARGS__) \
  DECLARE_CXX_WRAPPER(ret_type, func, __VA_ARGS__) \
  extern "C" \
  INTERCEPTOR_ATTRIBUTE \
  ret_type WRAP(func)(__VA_ARGS__)

# define DECLARE_REAL(ret_type, func, ...) \
    typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
    namespace __sanitizer { \
      extern FUNC_TYPE(func) PTR_TO_REAL(func); \
    }
# define ASSIGN_REAL(dst, src) REAL(dst) = REAL(src)

#define ASAN_INTERFACES_MACRO DECLARE_REAL
#include "asan_interfaces.inc"
#undef ASAN_INTERFACES_MACRO

#    define ASAN_INTERCEPT_FUNC(name)                                        \
      do {                                                                   \
        if (!INTERCEPT_FUNCTION(name))                                       \
          VReport(1, "AddressSanitizer: failed to intercept '%s'\n", #name); \
      } while (0)
#    define ASAN_INTERCEPT_FUNC_VER(name, ver)                           \
      do {                                                               \
        if (!INTERCEPT_FUNCTION_VER(name, ver))                          \
          VReport(1, "AddressSanitizer: failed to intercept '%s@@%s'\n", \
                  #name, ver);                                           \
      } while (0)
#    define ASAN_INTERCEPT_FUNC_VER_UNVERSIONED_FALLBACK(name, ver)           \
      do {                                                                    \
        if (!INTERCEPT_FUNCTION_VER(name, ver) && !INTERCEPT_FUNCTION(name))  \
          VReport(1,                                                          \
                  "AddressSanitizer: failed to intercept '%s@@%s' or '%s'\n", \
                  #name, ver, #name);                                         \
      } while (0)

#    define ASAN_INTERCEPT_FUNC_WITH_ADDR(name, addr)                        \
      do {                                                                   \
        if (!INTERCEPT_FUNCTION_WITH_ADDR(name, addr))                       \
          VReport(1, "AddressSanitizer: failed to intercept '%s'\n", #name); \
      } while (0)

extern "C" {

void InitializeAsanInterceptors();

}


#endif // SANITIZER_INTERCEPTORS_H